﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

using RRRSRoguelike.Enums;
using RRRSRoguelike.Screens;
using RRRSRoguelikeData;

namespace RRRSRoguelike.Managers
{
	/// <summary>
	/// Manages the game flow.
	/// </summary>
	class ApplicationManager
	{

		#region variables

		/// <summary>
		/// Time passed 
		/// </summary>
		public Stopwatch Time;

		/// <summary>
		///Game time per dungeon
		/// </summary>
		public Stopwatch GameTime;

		/// <summary>
		/// A list of stopwatches for general use.
		/// </summary>
		public Dictionary<string, Stopwatch> StopWatches = new Dictionary<string, Stopwatch>();

		/// <summary>
		/// A facade to load the screen stack in order
		/// </summary>
		public List<Screen> ScreenLoader = new List<Screen>();
		/// <summary>
		/// A basic screen manager 
		/// </summary>
		public Stack<Screen> ScreenStack = new Stack<Screen>();

		/// <summary>
		/// Keeps a list of all texts to be used in game menus outside of a dungeon instance
		/// </summary>
		public Dictionary<string, string> Texts = new Dictionary<string, string>();

		/// <summary>
		/// handles all input
		/// </summary>
		public InputManager InputManager;

		/// <summary>
		/// handles sound outside of a dungeon instance
		/// </summary>
		public SoundManager SoundManager;
		
		/// <summary>
		/// Holds all the information for the user and session
		/// </summary>
		public Profile Profile = new Profile();

		#endregion

		#region singleton
		static readonly ApplicationManager instance = new ApplicationManager();

		public static ApplicationManager Instance
		{
			get { return instance; }
		}
		#endregion

		#region methods
		/// <summary>
		/// Initializes the gameScreens.
		/// </summary>
		private void Initialize()
		{
			InitializeManagers();

			LoadContent();

			SetupTime();

			SetupScreens();
		}

		static void InitializeManagers()
		{
			Instance.SoundManager = new SoundManager();

			Instance.InputManager = new InputManager();
		}

		void LoadContent()
		{
			Texts.Add("Splash", Files.TxtFileToString("Content/Texts/Splash.txt"));
			
			//FIXME: display the credits as rows using the new row helper thingy.
			Texts.Add("Credits", Files.TxtFileToString("Content/Texts/Credits.txt"));
			Instance.SoundManager.AddSound("Pluck", "Content/Sound/Pluck.wav");
		}

		void SetupTime()
		{
			Time = new Stopwatch();
			Time.Start();
			GameTime = new Stopwatch();
		}

		/// <summary>
		///Used to design game flow.
		///Splash and menu screens come off the stack first, credit remains until exit, with no screens pushed on top.  
		/// </summary>
		void SetupScreens()
		{
			
			//Built a simple screenloader to improve the readability.
			//probably good practice to make sure the screen loader is empty first
			ScreenLoader.Clear();
			
			
			//HACK: we can comment out different screens here for testing
			//Screens can now be added in order.
			ScreenLoader.Add(new TextScreen(Texts["Splash"]));
			ScreenLoader.Add(new ProfileScreen());
			ScreenLoader.Add(new MenuScreen());
			ScreenLoader.Add(new TextScreen(Texts["Credits"]));
			
			//This loads the screens into the screen stack and clears the loader
			LoadFromScreenLoader();

		}

		/// <summary>
		/// Entry point. Controls the gameflow.
		/// </summary>
		public void Run()
		{
			Initialize();

			//This is the main engine or loop of the game and calls screens one by one to show to the user.
			while (ScreenStack.Count > 0)
			{
				//HACK: hard coded wait between screens.
				Wait(120);
				ScreenStack.Pop().Show();
			}
		}

		public void ChangeAnimSpeed()
		{
			Properties.Settings.Default.AnimationSpeed -= 10;
			if (Properties.Settings.Default.AnimationSpeed < 10)
				Properties.Settings.Default.AnimationSpeed = 100;

			Properties.Settings.Default.Save();

			SoundManager.PlaySound(SoundList.Pluck.ToString());
		}

		public bool ToggleSetting(bool toggle)
		{
			//Toggle
			toggle = !toggle;

			return toggle;
		}

		public void Exit()
		{
			//Exit console
			Environment.Exit(0);
		}
		
		public void LoadFromScreenLoader()
		{
			for(int i  = ScreenLoader.Count-1;i>-1;i--)
			{
				ScreenStack.Push( ScreenLoader[i]);
			}
			
			ScreenLoader.Clear();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="delay">in milliseconds</param>
		public void Wait(long delay)
		{
			long now =ApplicationManager.Instance.Time.ElapsedMilliseconds;
			while(ApplicationManager.Instance.Time.ElapsedMilliseconds < now+delay)
			{
			}
		}
		
		
		#endregion

	}
}
